﻿/*
 * @author: wizardc
 */

using Dou.GPU;
using Dou.GPU.Instancing;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;
using Unity.Mathematics;
using UnityEngine;
using UnityEngine.Rendering;

namespace Demo.WorldMap.GPU
{
    public enum GPUResLoadState
    {
        None,
        Request,
        Loading,
        Complete,
        Dispose
    }
    
    /// <summary>
    /// GPU 一个批次绘制信息
    /// 一颗树里的一个的Mesh和材质会对应一个批次绘制
    /// </summary>
    public unsafe class GPUBatchDraw
    {
        private const int MaxBatchCount = 256;
        private static readonly int NameId = Shader.PropertyToID("transformBuf");
        private const string UseGpuInstancing = "USE_GPU_INST";
        
        private GPUStaticRenderLODRes _lodRes;
        
        private GPUResLoadState _state = GPUResLoadState.None; // 当前状态
        
        private Mesh _mesh;
        private Material _material;
        
        private NativeList<byte> _tempBuf; // 未加载完成时记录的数据信息
        
        private int _batchId;
        private GPUPropertyData _property;
        
        public GPUResLoadState state => _state;
        
        public void Init(GPUStaticRenderLODRes lodRes)
        {
            _lodRes = lodRes;
            _tempBuf = new NativeList<byte>(Allocator.Persistent);
        }
        
        /// <summary>
        /// 请求加载资源
        /// 注意 Job 中不能执行加载接口，这里只记录状态
        /// </summary>
        public void LoadResIfNeed()
        {
            if (_state == GPUResLoadState.None)
            {
                _state = GPUResLoadState.Request;
            }
            else
            {
                if (_state == GPUResLoadState.Complete && _tempBuf.IsCreated)
                {
                    if (!_tempBuf.IsEmpty)
                    {
                        _property.nativeList.AddRange(_tempBuf);
                    }
                    _tempBuf.Dispose();
                }
            }
        }

        /// <summary>
        /// 尝试加载资源
        /// </summary>
        public void TryLoadRes()
        {
            if (_state == GPUResLoadState.Request)
            {
                _state = GPUResLoadState.Loading;
                WorldMapAssetUtil.LoadObject(_lodRes.meshName, (path, obj) =>
                {
                    if (_state == GPUResLoadState.Dispose)
                    {
                        WorldMapAssetUtil.ReleaseObject(path);
                        return;
                    }
                    _mesh = (Mesh) obj;
                    OnLoadComplete();
                }, true);
                WorldMapAssetUtil.LoadObject(_lodRes.matName, (path, obj) =>
                {
                    if (_state == GPUResLoadState.Dispose)
                    {
                        WorldMapAssetUtil.ReleaseObject(path);
                        return;
                    }
                    _material = (Material) obj;
                    // 开启 GPU Instancing 绘制的状态，美术开发场景时，该状态是关闭的，运行时由程序打开
                    // 注意：这里在Unity编辑器下存在bug，设置后可能不生效，需要重新运行一次才生效，美术编辑时如果看不到也需要重新打开一次场景
                    // 解决方法可以在导出工具中，直接拷贝一份材质并设定好该宏，使美术使用的材质和运行的材质直接分离来解决该问题
                    CoreUtils.SetKeyword(_material, UseGpuInstancing, true);
                    OnLoadComplete();
                }, true);
            }
        }
        
        private void OnLoadComplete()
        {
            if (_mesh != null && _material != null)
            {
                var gpuInst = GPUInstancingMgr.instance;
                
                _batchId = gpuInst.RegisterBatch(_mesh, _material, MaxBatchCount, ShadowCastingMode.On, true);
                gpuInst.RegisterProperty(_batchId, NameId, GPUDefine.Stride_Float4x4);
                
                _property = gpuInst.GetProperty(_batchId, NameId);
                
                _state = GPUResLoadState.Complete;
            }
        }
        
        /// <summary>
        /// 清除所有显示对象
        /// </summary>
        public void ClearTransform()
        {
            if (_tempBuf.IsCreated)
            {
                _tempBuf.Clear();
            }
            else
            {
                _property.nativeList.Clear();
            }
        }
        
        /// <summary>
        /// 添加一个显示对象
        /// </summary>
        public void AddTransform(void* ptr, int count)
        {
            // 调试
            // var mat = (Unity.Mathematics.float4x4*) ptr;
            // Debug.Log(mat->c3);
            
            if (_tempBuf.IsCreated)
            {
                _tempBuf.AddRange(ptr, count);
            }
            else
            {
                _property.nativeList.AddRange(ptr, count);
            }
        }
        
        /// <summary>
        /// 销毁
        /// </summary>
        public void Dispose()
        {
            _state = GPUResLoadState.Dispose;
            if (_tempBuf.IsCreated)
            {
                _tempBuf.Dispose();
            }
            if (_mesh != null)
            {
                WorldMapAssetUtil.ReleaseObject(_lodRes.meshName);
            }
            if (_material != null)
            {
                WorldMapAssetUtil.ReleaseObject(_lodRes.matName);
            }
            GPUInstancingMgr.instance.UnregisterBatch(_batchId);
        }
        
#if UNITY_EDITOR

        public void DrawObjAABB()
        {
            float4x4* ptr;
            int len;
            if (_tempBuf.IsCreated)
            {
                ptr = (float4x4*) _tempBuf.GetUnsafePtr();
                len = _tempBuf.Length;
            }
            else
            {
                ptr = (float4x4*) _property.nativeList.GetUnsafePtr();
                len = _property.nativeList.Length;
            }
            
            var num = len / GPUDefine.Stride_Float4x4;
            for (int j = 0; j < num; j++)
            {
                var p = *(ptr + j);
                Gizmos.DrawWireCube(new Vector3(p.c3.x, p.c3.y, p.c3.z), new Vector3(p.c0.w * 2, p.c1.w * 2, p.c0.w * 2));
            }
        }
        
#endif

    }
}
